/*
 * Copyright (C) 2014 jsonwebtoken.io
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.apache.jwt.impl;

public class Base64UrlCodec extends AbstractTextCodec {

	public String encode(byte[] data) {
		String base64Text = TextCodec.BASE64.encode(data);
		byte[] bytes = base64Text.getBytes(US_ASCII);

		//base64url encoding doesn't use padding chars:
		bytes = removePadding(bytes);

		//replace URL-unfriendly Base64 chars to url-friendly ones:
		for (int i = 0; i < bytes.length; i++) {
			if (bytes[i] == '+') {
				bytes[i] = '-';
			} else if (bytes[i] == '/') {
				bytes[i] = '_';
			}
		}

		return new String(bytes, US_ASCII);
	}

	protected byte[] removePadding(byte[] bytes) {

		byte[] result = bytes;

		int paddingCount = 0;
		for (int i = bytes.length - 1; i > 0; i--) {
			if (bytes[i] == '=') {
				paddingCount++;
			} else {
				break;
			}
		}
		if (paddingCount > 0) {
			result = new byte[bytes.length - paddingCount];
			System.arraycopy(bytes, 0, result, 0, bytes.length - paddingCount);
		}

		return result;
	}

	public byte[] decode(String encoded) {
		char[] chars = encoded.toCharArray(); //always ASCII - one char == 1 byte

		//Base64 requires padding to be in place before decoding, so add it if necessary:
		chars = ensurePadding(chars);

		//Replace url-friendly chars back to normal Base64 chars:
		for (int i = 0; i < chars.length; i++) {
			if (chars[i] == '-') {
				chars[i] = '+';
			} else if (chars[i] == '_') {
				chars[i] = '/';
			}
		}

		String base64Text = new String(chars);

		return TextCodec.BASE64.decode(base64Text);
	}

	protected char[] ensurePadding(char[] chars) {

		char[] result = chars; //assume argument in case no padding is necessary

		int paddingCount = 0;

		//fix for https://github.com/jwtk/jjwt/issues/31
		int remainder = chars.length % 4;
		if (remainder == 2 || remainder == 3) {
			paddingCount = 4 - remainder;
		}

		if (paddingCount > 0) {
			result = new char[chars.length + paddingCount];
			System.arraycopy(chars, 0, result, 0, chars.length);
			for (int i = 0; i < paddingCount; i++) {
				result[chars.length + i] = '=';
			}
		}

		return result;
	}

}
